/*  
	------------------------------------------------------------------------
	 NQ Core Library
	
	 Homepage: http://www.awzhome.de/
	------------------------------------------------------------------------
	
	This Source Code Form is subject to the terms of the Mozilla Public
	License, v. 2.0. If a copy of the MPL was not distributed with this
	file, You can obtain one at http://mozilla.org/MPL/2.0/.
   
	The Original Code is code of NQ Core Library.

	The Initial Developer of the Original Code is Andreas Weizel.
	Portions created by the Initial Developer are
	Copyright (C) 2012 Andreas Weizel. All Rights Reserved.
	
	Contributor(s): (none)
	
	------------------------------------------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

namespace AWZhome.NQ.Core
{

	internal class NQServiceInfo : INQServiceInfo
	{
		internal NQServiceInfo(Type serviceType, string compName)
		{
			List<Type> nqInterfaces = new List<Type>();

			this.ParentComponent = compName;
			this.ServiceType = serviceType;
			NQExportedServiceAttribute exportedServiceAttr =
				(NQExportedServiceAttribute) Attribute.GetCustomAttribute(serviceType, typeof(NQExportedServiceAttribute));
			NQServiceSubstituteAttribute serviceSubstAttr =
				(NQServiceSubstituteAttribute) Attribute.GetCustomAttribute(serviceType, typeof(NQServiceSubstituteAttribute));
			NQServiceAttachmentAttribute serviceAttachmentAttr =
				(NQServiceAttachmentAttribute) Attribute.GetCustomAttribute(serviceType, typeof(NQServiceAttachmentAttribute));
			NQComponentRequirementAttribute[] compRequirementAttr =
				(NQComponentRequirementAttribute[]) Attribute.GetCustomAttributes(serviceType, typeof(NQComponentRequirementAttribute));
			NQAutoInjectionAttribute[] autoInjectionAttr = (NQAutoInjectionAttribute[]) Attribute.GetCustomAttributes(serviceType, typeof(NQAutoInjectionAttribute));
			if (exportedServiceAttr != null)
			{
				// Save component information
				this.SingleInstance = exportedServiceAttr.SingleInstance;
				if (serviceSubstAttr != null)
				{
					this.Substitutes = serviceSubstAttr.ServiceList;
				}
				if (serviceAttachmentAttr != null)
				{
					this.AttachedTo = serviceAttachmentAttr.ServiceList;
				}
				this.ServiceCategory = exportedServiceAttr.Category;
				if (compRequirementAttr != null)
				{
					this.Requires = new INQComponentRequirement[compRequirementAttr.Length];
					for (int i = 0; i < compRequirementAttr.Length; i++)
					{
						this.Requires[i] = compRequirementAttr[i].ComponentRequirement;
					}
				}

				// Get the registered interface for the service
				Type[] types = serviceType.GetInterfaces();
				if ((types != null) && (types.Length > 0))
				{
					// Take the first interface, that is marked with NQServiceInterfaceAttribute
					foreach (Type interfaceType in serviceType.GetInterfaces())
					{
						NQServiceInterfaceAttribute serviceInterfaceAttr =
							(NQServiceInterfaceAttribute) Attribute.GetCustomAttribute(interfaceType, typeof(NQServiceInterfaceAttribute));
						if (serviceInterfaceAttr != null)
						{
							this.ServiceInterface = interfaceType;
							break;
						}
					}
				}

				if (this.ServiceInterface == null)
				{
					// We use the type implementing the service as its own service interface
					this.ServiceInterface = serviceType;
				}

				// List all interfaces that are implemented by the registered service interface
				foreach (Type interfaceType in this.ServiceInterface.GetInterfaces())
				{
					//NQServiceInterfaceAttribute typesia = (NQServiceInterfaceAttribute) Attribute.GetCustomAttribute(interfaceType, typeof(NQServiceInterfaceAttribute));
					//if (typesia != null)
					//{
					nqInterfaces.Add(interfaceType);
					//}
				}
				// Convert list to array
				this.InterfaceTypes = new Type[nqInterfaces.Count];
				nqInterfaces.CopyTo(this.InterfaceTypes);

				// Look for invokable service methods
				if (typeof(INQInvokableService).IsAssignableFrom(serviceType))
				{
					// We get MethodInfo objects for the INQInvokableService methods InvokeService and QuitService
					MethodInfo invokeMethod = serviceType.GetMethod("InvokeService");
					if (invokeMethod != null)
					{
						this.InvokeMethod = invokeMethod;
					}

					MethodInfo quitMethod = serviceType.GetMethod("QuitService");
					if (quitMethod != null)
					{
						this.QuitMethod = quitMethod;
					}

					this.IsInvokable = true;
				}
				else
				{
					// The service doesn't implement INQInvokableService 
					MethodInfo[] methods = serviceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
					if (methods != null)
					{
						for (int i = 0; i < methods.Length; i++)
						{
							NQServiceInvokeMethodAttribute invokeMethodAttr = (NQServiceInvokeMethodAttribute) Attribute.GetCustomAttribute(methods[i], typeof(NQServiceInvokeMethodAttribute));
							NQServiceQuitMethodAttribute quitMethodAttr = (NQServiceQuitMethodAttribute) Attribute.GetCustomAttribute(methods[i], typeof(NQServiceQuitMethodAttribute));

							if (invokeMethodAttr != null)
							{
								// We have found an InvokeMethod
								this.InvokeMethod = methods[i];
								this.IsInvokable = true;
							}
							if (quitMethodAttr != null)
							{
								// We have found a QuitMethod
								this.QuitMethod = methods[i];
								this.IsInvokable = true;
							}
						}
					}
				}

				// Now check for auto-injection entries
				if (autoInjectionAttr != null)
				{
					this.AutoInjections = new INQAutoInjection[autoInjectionAttr.Length];
					for (int i = 0; i < autoInjectionAttr.Length; i++)
					{
						this.AutoInjections[i] = new NQAutoInjection()
							{
								BoundService = autoInjectionAttr[i].BoundService,
								BoundAttachList = autoInjectionAttr[i].BoundAttachList,
								Interface = autoInjectionAttr[i].ServiceInterface,
								InjectAsDependent = autoInjectionAttr[i].InjectAsDependent,
								InjectFromAttachList = autoInjectionAttr[i].InjectFromAttachList,
								Overridable = autoInjectionAttr[i].Overridable
							};
					}
				}
			}
			else
			{
				// Erroneous service definition
				throw new NQServiceDefinitionException(NQErrorReason.ServiceNameUnspecified, "Service name unspecified");
			}
		}

		public static bool IsValidServiceClass(Type serviceType)
		{
			NQExportedServiceAttribute esa = (NQExportedServiceAttribute) Attribute.GetCustomAttribute(serviceType, typeof(NQExportedServiceAttribute));
			return (esa != null);
		}

		#region INQServiceInfo Members

		public MethodInfo QuitMethod
		{
			get;
			private set;
		}


		public MethodInfo InvokeMethod
		{
			get;
			private set;
		}

		public Type ServiceInterface
		{
			get;
			private set;
		}

		public string ParentComponent
		{
			get;
			private set;
		}

		public bool SingleInstance
		{
			get;
			private set;
		}

		public Type[] Substitutes
		{
			get;
			private set;
		}

		public Type ServiceType
		{
			get;
			private set;
		}

		public string[] AttachedTo
		{
			get;
			private set;
		}

		public INQComponentRequirement[] Requires
		{
			get;
			private set;
		}

		public NQHostMode ServiceCategory
		{
			get;
			private set;
		}

		public Type[] InterfaceTypes
		{
			get;
			private set;
		}

		public bool IsInvokable
		{
			get;
			private set;
		}

		public INQAutoInjection[] AutoInjections
		{
			get;
			private set;
		}

		#endregion

	}

}